package model

import (
	"ginblog/utils/errmsg"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"log"
)

// User 用户模型
type User struct {
	gorm.Model
	Username string `gorm:"type:varchar(20);not null " json:"username" validate:"required,min=4,max=12" label:"用户名"`
	Password string `gorm:"type:varchar(500);not null" json:"password" validate:"required,min=6,max=120" label:"密码"`
	Role     int    `gorm:"type:int;DEFAULT:2" json:"role" validate:"required,gte=2" label:"角色码"`
}

// CheckUser 查询用户是否存在
// CheckUser 检查用户名是否已被使用
// 参数:
//
//	name - 要检查的用户名
//
// 返回值:
//
//	code - 状态码，表示用户名是否已被使用
func CheckUser(name string) (code int) {
	// 初始化User结构体
	var user User

	// 查询数据库中是否存在相同的用户名
	// 仅选择id字段以提高查询效率
	db.Select("id").Where("username = ?", name).First(&user)

	// 判断查询结果
	// 如果user.ID大于0，说明用户名已被使用
	if user.ID > 0 {
		// 返回错误码，表示用户名已被使用
		return errmsg.ERROR_USERNAME_USED //1001
	}

	// 如果user.ID不大于0，说明用户名未被使用
	// 返回成功码
	return errmsg.SUCCESS
}

// CheckUpUser 更新查询
// CheckUpUser 检查用户名是否已被使用。
// 参数:
//
//	id - 用户ID。
//	name - 用户名。
//
// 返回值:
//
//	code - 状态码，表示用户名检查的结果。
func CheckUpUser(id int, name string) (code int) {
	// 初始化User变量，用于存储从数据库中查询的结果。
	var user User

	// 从数据库中查询指定用户名的用户信息，仅选择id和username字段。
	// 这里使用Select和Where来限制查询的字段和条件。
	db.Select("id, username").Where("username = ?", name).First(&user)

	// 检查查询到的用户ID是否与传入的ID匹配。
	// 如果匹配，表示用户名未被使用，返回成功状态码。
	if user.ID == uint(id) {
		return errmsg.SUCCESS
	}

	// 如果查询到的用户ID大于0，表示用户名已被使用，返回错误状态码。
	if user.ID > 0 {
		return errmsg.ERROR_USERNAME_USED //1001
	}

	// 如果以上条件都不满足，表示用户名未被使用，返回成功状态码。
	return errmsg.SUCCESS
}

// CreateUser 新增用户
// CreateUser 在数据库中创建一个新用户
// 参数:
//
//	data *User: 包含用户信息的指针
//
// 返回值:
//
//	int: 表示操作结果的错误码，成功返回errmsg.SUCCESS，失败返回errmsg.ERROR
func CreateUser(data *User) int {
	// 使用ScryptPw方法加密用户密码
	//data.Password = ScryptPw(data.Password)

	// 在数据库中创建用户
	err := db.Create(&data).Error
	// 如果创建过程中出现错误
	if err != nil {
		// 返回错误码表示创建失败
		return errmsg.ERROR // 500
	}

	// 如果用户成功创建，返回错误码表示成功
	return errmsg.SUCCESS
}

// GetUser 查询用户
// GetUser 根据用户ID获取用户信息。
// 参数:
//
//	id - 用户的唯一标识符。
//
// 返回值:
//
//	User - 用户信息结构体，包含用户的相关信息。
//	int - 错误代码，用于指示操作是否成功。
func GetUser(id int) (User, int) {
	// 初始化一个User实例，用于存储查询到的用户信息。
	var user User

	// 执行数据库查询操作，限制查询结果为1条，并指定查询条件为用户ID等于给定的id。
	// 如果查询过程中出现错误，将错误信息存储在err中。
	err := db.Limit(1).Where("ID = ?", id).Find(&user).Error

	// 检查查询过程中是否有错误发生。
	// 如果有错误，返回当前用户信息（可能是空的）和错误代码errmsg.ERROR，指示查询失败。
	if err != nil {
		return user, errmsg.ERROR
	}

	// 如果查询成功且没有错误，返回查询到的用户信息和错误代码errmsg.SUCCESS，指示查询成功。
	return user, errmsg.SUCCESS
}

// GetUsers 查询用户列表
// GetUsers 根据用户名、页面大小和页数获取用户列表和总用户数。
// 如果提供了用户名，则返回匹配该用户名前缀的用户列表；否则，返回所有用户列表。
// 参数:
//
//	username - 用户名前缀，用于搜索用户。如果为空，返回所有用户。
//	pageSize - 每页的用户数量。
//	pageNum - 当前页数。
//
// 返回值:
//
//	[]User - 用户列表。
//	int64 - 符合条件的总用户数。
func GetUsers(username string, pageSize int, pageNum int) ([]User, int64) {
	var users []User
	var total int64

	// 当提供了用户名前缀时，查询匹配该前缀的用户列表和总用户数。
	if username != "" {
		// 查询匹配用户名前缀的用户列表。
		db.Select("id,username,role,created_at").Where(
			"username LIKE ?", username+"%",
		).Limit(pageSize).Offset((pageNum - 1) * pageSize).Find(&users)
		// 查询匹配用户名前缀的总用户数。
		db.Model(&users).Where(
			"username LIKE ?", username+"%",
		).Count(&total)
		return users, total
	}
	// 当未提供用户名前缀时，查询所有用户列表和总用户数。
	db.Select("id,username,role,created_at").Limit(pageSize).Offset((pageNum - 1) * pageSize).Find(&users)
	db.Model(&users).Count(&total)

	// 如果发生错误，返回空用户列表和零总用户数。
	if err != nil {
		return users, 0
	}
	// 返回用户列表和总用户数。
	return users, total
}

// EditUser 编辑用户信息
// EditUser 编辑用户信息
// 参数:
//
//	id - 用户ID，用于确定要编辑的用户
//	data - 指向包含新用户信息的结构体指针
//
// 返回值:
//
//	编辑操作的状态码，成功返回errmsg.SUCCESS，失败返回errmsg.ERROR
func EditUser(id int, data *User) int {
	// 初始化一个空的User结构体用于接收数据库中的用户信息
	var user User
	// 创建一个空的map用于存储需要更新的用户信息字段
	var maps = make(map[string]interface{})
	// 将需要更新的用户名字段添加到map中
	maps["username"] = data.Username
	// 将需要更新的用户角色字段添加到map中
	maps["role"] = data.Role
	// 使用db对象的Model方法指定要更新的表，Where方法指定条件，Updates方法应用更新
	err = db.Model(&user).Where("id = ? ", id).Updates(maps).Error
	// 如果更新操作中发生错误，返回错误码
	if err != nil {
		return errmsg.ERROR
	}
	// 如果更新操作成功，返回成功码
	return errmsg.SUCCESS
}

// ChangePassword 修改密码
// ChangePassword 根据用户ID更改用户密码。
// 参数:
//
//	id - 用户ID。
//	data - 包含新密码的用户数据指针。
//
// 返回值:
//
//	修改成功返回errmsg.SUCCESS，修改失败返回errmsg.ERROR。
func ChangePassword(id int, data *User) int {
	//var user User
	//var maps = make(map[string]interface{})
	//maps["password"] = data.Password

	// 使用db进行密码更改操作，仅更新密码字段。
	err = db.Select("password").Where("id = ?", id).Updates(&data).Error
	if err != nil {
		// 如果更新操作出错，返回错误代码。
		return errmsg.ERROR
	}
	// 如果更新操作成功，返回成功代码。
	return errmsg.SUCCESS
}

// DeleteUser 删除用户
// DeleteUser 根据ID删除用户。
// 参数 id: 要删除的用户的唯一标识符。
// 返回值: 返回执行状态。如果删除失败，返回 errmsg.ERROR；如果成功，返回 errmsg.SUCCESS。
func DeleteUser(id int) int {
	// 初始化一个 User 变量以存储用户信息。
	var user User
	// 使用用户的ID查询并删除数据库中的用户记录。
	err = db.Where("id = ? ", id).Delete(&user).Error
	// 如果删除操作遇到错误，返回 errmsg.ERROR。
	if err != nil {
		return errmsg.ERROR
	}
	// 如果删除操作成功，返回 errmsg.SUCCESS。
	return errmsg.SUCCESS
}

// BeforeCreate 密码加密&权限控制
// BeforeCreate 是 User 类型的一个方法，在创建新用户实例前被调用。
// 该方法主要用于执行保存用户实例到数据库前的预处理操作。
// 参数:
//
//	db *gorm.DB: GORM数据库连接对象，未使用。
//
// 返回值:
//
//	err error: 可能发生的错误，本方法中不会返回错误。
func (u *User) BeforeCreate(_ *gorm.DB) (err error) {
	// 使用ScryptPw函数加密用户密码，确保密码安全存储。
	u.Password = ScryptPw(u.Password)
	// 设置用户角色ID为2，通常代表普通用户角色。
	u.Role = 2
	// 没有错误发生，返回nil。
	return nil
}

// BeforeUpdate 密码加密
// BeforeUpdate 在更新User对象之前执行一些操作。
// 这个方法主要用来在更新数据库记录之前对用户密码进行加密。
// 参数:
//
//	_ *gorm.DB: 未使用，预留参数，供将来可能的使用。
//
// 返回值:
//
//	err error: 如果有错误发生，返回错误信息。
func (u *User) BeforeUpdate(_ *gorm.DB) (err error) {
	// 使用ScryptPw函数加密用户密码。
	u.Password = ScryptPw(u.Password)
	// 没有错误发生，返回nil。
	return nil
}

// ScryptPw 生成密码
// ScryptPw 使用bcrypt算法对密码进行散列处理。
// 该函数接收一个字符串类型的password作为输入，返回散列后的密码字符串。
// 使用bcrypt算法可以有效地增加密码破解的难度，保护用户密码安全。
func ScryptPw(password string) string {
	// 定义bcrypt算法的cost参数，表示算法的复杂度。
	// cost值越大，计算散列所需的时间和计算量越大，安全性越高，但性能消耗也越大。
	const cost = 10

	// GenerateFromPassword是bcrypt库提供的函数，用于生成散列后的密码。
	// 它接受两个参数：一个是待散列的密码（作为byte切片传入），另一个是cost参数。
	// 返回值HashPw是散列后的密码，err用于指示是否发生了错误。
	HashPw, err := bcrypt.GenerateFromPassword([]byte(password), cost)
	if err != nil {
		// 如果生成散列密码时发生错误，记录错误并终止程序。
		// 使用log.Fatal记录错误并退出是因为散列密码是安全存储密码的关键步骤，
		// 如果这一步失败，程序应立即停止以避免进一步的错误处理。
		log.Fatal(err)
	}

	// 返回散列后的密码字符串。
	// 将byte切片类型的HashPw转换为字符串类型，以便于存储和传输。
	return string(HashPw)
}

// CheckLogin 后台登录验证
// CheckLogin 检查用户名和密码是否匹配。
// 参数:
//
//	username - 用户名字符串。
//	password - 密码字符串。
//
// 返回值:
//
//	User - 用户信息结构体。
//	int - 错误代码，表示登录过程中的不同错误状态。
func CheckLogin(username string, password string) (User, int) {
	// 初始化用户变量和密码错误变量。
	var user User
	var PasswordErr error

	// 查询数据库中是否存在匹配用户名的用户。
	db.Where("username = ?", username).First(&user)

	// 比较数据库中的密码哈希值和输入的密码。
	PasswordErr = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))

	// 如果用户ID为0，表示用户不存在。
	if user.ID == 0 {
		return user, errmsg.ERROR_USER_NOT_EXIST
	}
	// 如果密码比较出错，表示密码错误。
	if PasswordErr != nil {
		return user, errmsg.ERROR_PASSWORD_WRONG
	}
	// 如果用户角色不是1，表示用户没有权限。
	if user.Role != 1 {
		return user, errmsg.ERROR_USER_NO_RIGHT
	}
	// 用户验证成功，返回用户信息和成功错误码。
	return user, errmsg.SUCCESS
}

// CheckLoginFront 前台登录
// CheckLoginFront 检查前端用户登录信息。
// 参数:
//
//	username - 用户名。
//	password - 密码。
//
// 返回值:
//
//	User - 用户信息结构体。
//	int - 错误代码，表示登录结果。
func CheckLoginFront(username string, password string) (User, int) {
	// 初始化用户结构体。
	var user User
	// 初始化密码错误变量。
	var PasswordErr error

	// 查询数据库中是否存在该用户名的用户。
	db.Where("username = ?", username).First(&user)

	// 比较数据库中的密码哈希值和输入的密码。
	PasswordErr = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
	// 如果用户ID为0，表示用户不存在。
	if user.ID == 0 {
		return user, errmsg.ERROR_USER_NOT_EXIST
	}
	// 如果密码比较出错，表示密码错误。
	if PasswordErr != nil {
		return user, errmsg.ERROR_PASSWORD_WRONG
	}
	// 用户名和密码匹配，返回用户信息和成功代码。
	return user, errmsg.SUCCESS
}
